﻿using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json;
using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

namespace SCM.Common.Extensions
{
    public class RedisHelper : IRedisHelper
    {
        ConnectionMultiplexer connection;
        private object _locker = new object();
        private string connStr;
        private int dbNumber = -1;

        public void Init(string connStr, int dbNumber)
        {
            this.connStr = connStr;
            this.dbNumber = dbNumber;
        }
        public ConnectionMultiplexer Manager
        {
            get
            {
                if (connection == null)
                {
                    lock (_locker)
                    {
                        if (connection != null)
                        {
                            if (connection.IsConnected)
                            {
                                return connection;
                            }
                            else
                            {
                                connection.Dispose();
                            }
                        }
                        connection = GetManager();
                        return connection;
                    }
                }
                return connection;
            }
        }
        public IDatabase Database
        {
            get { return GetDatabase(); }
        }
        private ConnectionMultiplexer GetManager()
        {
            if (string.IsNullOrWhiteSpace(connStr))
            {
                throw new Exception("未配置连接Redis");
            }

            return ConnectionMultiplexer.Connect(connStr);
        }

        public IDatabase GetDatabase()
        {
            return Manager.GetDatabase(dbNumber);
        }
        public bool Remove(string key)
        {
            var db = GetDatabase();
            return db.KeyDelete(key);
        }

        public string Get(string key)
        {
            var db = GetDatabase();
            return db.StringGet(key);
        }
        public T Get<T>(string key)
        {
            string str = Get(key);
            if (string.IsNullOrWhiteSpace(str))
            {
                return default(T);
            }
            return JsonConvert.DeserializeObject<T>(str);
        }
        public bool Set(string key, string value, int expireSeconds = 0)
        {
            var db = GetDatabase();
            if (expireSeconds > 0)
            {
                return db.StringSet(key, value, TimeSpan.FromSeconds(expireSeconds));
            }
            else
            {
                return db.StringSet(key, value);
            }
        }

        public string GetHash(string key, string hashField)
        {
            IDatabase db = GetDatabase();
            return db.HashGet(key, hashField);
        }

        public T GetHash<T>(string key, string hashField)
        {
            string json = GetHash(key, hashField);
            if (!string.IsNullOrWhiteSpace(json))
            {
                return JsonConvert.DeserializeObject<T>(json);
            }
            return default(T);
        }
        public RedisValue[] GetHash(string key)
        {
            IDatabase db = GetDatabase();
            return db.HashValues(key);
        }


        public List<T> GetHashList<T>(string key)
        {
            RedisValue[] redis = GetHash(key);
            if (redis.Length == 0)
                return null;
            return JsonConvert.DeserializeObject<List<T>>(CoverJson(redis));
        }

        private string CoverJson(RedisValue[] arr)
        {
            StringBuilder json = new StringBuilder("[");
            foreach (string str in arr)
            {
                json.Append(str);
                json.Append(",");
            }
            json.Remove(json.Length - 1, 1);
            json.Append("]");
            return json.ToString();
        }
        public bool SetHash(string key, string hashField, RedisValue value, int expireSeconds = 0)
        {
            IDatabase db = GetDatabase();
            bool b = db.HashSet(key, hashField, value);
            if (expireSeconds > 0)
                db.KeyExpire(key, TimeSpan.FromSeconds(expireSeconds));
            return b;
        }

        public bool RefreshTimeOut(string key, int expireSeconds)
        {
            return GetDatabase().KeyExpire(key, TimeSpan.FromSeconds(expireSeconds));
        }
        public bool HasKey(string key)
        {
            var db = GetDatabase();
            return db.KeyExists(key);
        }
        /// <summary>
        /// 分布式锁
        /// </summary>
        /// <param name="key">锁标识</param>
        /// <param name="token">锁拥有者</param>
        /// <param name="expireMinutes"></param>
        /// <returns></returns>
        public void Lock(string key, string token, int expireMinutes)
        {
            IDatabase db = GetDatabase();
            while (true)
            {
                if (db.LockTake(key, token, TimeSpan.FromSeconds(expireMinutes)))
                {
                    return;
                }
                Thread.Sleep(10);
            }
        }
        public bool LockTake(string key, string token, int expireSeconds)
        {
            IDatabase db = GetDatabase();
            return db.LockTake(key, token, TimeSpan.FromSeconds(expireSeconds));
        }
        /// <summary>
        /// 释放锁
        /// </summary>
        /// <param name="key"></param>
        /// <param name="token"></param>
        /// <returns></returns>
        public bool LockRelease(string key, string token)
        {
            return GetDatabase().LockRelease(key, token);
        }


    }
}
